// 创建一个自定义的Canvas图层
const LeafletCanvasLine = L.Canvas.extend({
  lines: [], // 存储线段数据的数组
  lineStyle: {
    // 线条样式
    color: 'red',
    weight: 2,
  },
  /**
   * 提示报错信息
   **/
  promptInfo: {
    layerid_null: 'Layer Id cannot be empty',
    layers_no: 'Current layer not found',
    layers_data_none: 'Data cannot be empty',
    layers_min_length: 'At least two pieces of data are required',
  },
  onAdd: function(map) {
    // 创建一个新的Canvas元素
    this._canvas = L.DomUtil.create('canvas',
        'leaflet-layer leaflet-canvas-icon-layer');
    window.LeafletCanvasLine = this;
    this._ctx = this._canvas.getContext('2d');
    var animated = this._map.options.zoomAnimation && L.Browser.any3d;
    L.DomUtil.addClass(
        this._canvas,
        'leaflet-zoom-' + (animated ? 'animated' : 'hide'),
    );
    // 将Canvas元素添加到地图上
    map._panes.overlayPane.appendChild(this._canvas);
    this._reset(); // 更新Canvas内容
  },
  onRemove: function(map) {
    this.removeAllLayer();
    map.getPanes().overlayPane.removeChild(this._canvas);
  },
  _update: function() {
    var p = 0,
        size = this._map.getSize(),
        min = this._map.containerPointToLayerPoint(size.multiplyBy(-p)).round();
    this._bounds = new L.Bounds(
        min,
        min.add(size.multiplyBy(1 + p * 2)).round(),
    );
    this._center = this._map.getCenter();
    this._zoom = this._map.getZoom();
    this._updateCanvas(true);
  },
  /**
   * 获取事件对象
   *
   * 表示给map添加的监听器
   * @return {Object} 监听器/函数键值对
   */
  getEvents: function() {
    var events = {
      viewreset: this._reset,
      zoom: this._onZoom,
      moveend: this._reset,
    };
    if (this._zoomAnimated) {
      events.zoomanim = this._onAnimZoom;
    }
    return events;
  },
  /**
   * 地图Zoomanim事件监听器函数
   * @param ev
   */
  _onAnimZoom(ev) {
    this._updateTransform(ev.center, ev.zoom);
  },
  /**
   * 地图修改zoom事件监听器函数
   */
  _onZoom: function() {
    this._updateTransform(this._map.getCenter(), this._map.getZoom());
  },
  /**
   * 重置画布(大小,位置,内容)
   */
  _reset: function() {
    var topLeft = this._map.containerPointToLayerPoint([0, 0]);
    L.DomUtil.setPosition(this._canvas, topLeft);
    var size = this._map.getSize();
    this._canvas.width = size.x;
    this._canvas.height = size.y;
    this._update();
  },

  /**
   * 修改dom原始的transform或position
   * @param {L/LatLng} center 中心点
   * @param {number} zoom 地图缩放级别
   */
  _updateTransform: function(center, zoom) {
    var scale = this._map.getZoomScale(zoom, this._zoom),
        position = L.DomUtil.getPosition(this._canvas),
        viewHalf = this._map.getSize().multiplyBy(0.5),
        currentCenterPoint = this._map.project(this._map.getCenter(), zoom),
        destCenterPoint = this._map.project(center, zoom),
        centerOffset = destCenterPoint.subtract(currentCenterPoint),
        topLeftOffset = viewHalf.multiplyBy(-scale).
            add(position).
            add(viewHalf).
            subtract(centerOffset);

    if (L.Browser.any3d) {
      L.DomUtil.setTransform(this._canvas, topLeftOffset, scale);
    } else {
      L.DomUtil.setPosition(this._canvas, topLeftOffset);
    }
  },

  _updateCanvas: function(clear) {
    // 在Canvas上绘制内容
    if (clear) {
      this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height);
    }
    // 绘制线段
    for (var i = 0; i < this.lines.length; i++) {
      var line = this.lines[i];
      this._ctx.strokeStyle = line.color || this.lineStyle.color;
      this._ctx.lineWidth = line.weight || this.lineStyle.weight;
      for (var j = 0; j < line.coordinates.length - 1; j++) {
        var startLatLng = line.coordinates[j];
        var endLatLng = line.coordinates[j + 1];
        var startPixel = this._map.latLngToContainerPoint(startLatLng);
        var endPixel = this._map.latLngToContainerPoint(endLatLng);
        this._ctx.beginPath();
        this._ctx.moveTo(startPixel.x, startPixel.y);
        this._ctx.lineTo(endPixel.x, endPixel.y);
        this._ctx.stroke();
      }
    }
  },
  /**
   * 添加线
   * @param {String} id - 图层id
   * @param {Object} config - 基础配置
   * @param {Array} config.data - 经纬度数据 [[lat,lon]]
   * @param {String} config.color - 线条颜色
   * @param {String} config.weight - 线条粗细
   */
  addLine: function(
      id = '',
      config = {
        data: [],
        color: '#fff',
        weight: 1,
      },
  ) {
    try {
      let line = {
        id: id,
        coordinates: config.data || [],
        color: config.color || '#ffffff',
        weight: config.weight || 1,
        lastCoordinates: [], // 记录上一次绘制的坐标点
      };
      if (line.coordinates.length === 0) {
        throw new Error(this.promptInfo.layers_data_none);
      }
      if (line.coordinates.length > 0 && line.coordinates.length < 2) {
        throw new Error(this.promptInfo.layers_min_length);
      }
      this.lines.push(line);
      this._updateCanvas();
    } catch (err) {
      console.error(err);
    }
  },
  /**
   * 删除某个图层线条
   * @param {String} layerId - 线条图层ID
   **/
  removeLayer: function(layerId) {
    try {
      if (!layerId) {
        throw new Error(this.promptInfo.layerid_null);
      }
      let index = this.lines.findIndex((e) => e.id === layerId);
      if (index !== -1) {
        this.lines.splice(index, 1);
        this._updateCanvas(true);
      } else {
        throw new Error(this.promptInfo.layers_no);
      }
    } catch (error) {
      console.error(error);
    }
  },
  /**
   * 删除当前所有图层
   **/
  removeAllLayer() {
    this.lines = []; // 清空线条数组
    this._updateCanvas(true); // 更新Canvas内容
  },
  /**
   * 替换之前数据
   * @param {String} layerId 线条索引
   * @param {Array} newCoordinates 新坐标
   **/
  replaceLineData(layerId, newCoordinates = []) {
    try {
      if (!layerId) {
        throw new Error(this.promptInfo.layerid_null);
      }
      if (layerId && newCoordinates.length > 0) {
        if (this.lines.length > 0) {
          let index = this.lines.findIndex((e) => e.id === layerId);
          if (index !== -1) {
            if (newCoordinates.length > 0 && newCoordinates.length < 2) {
              throw new Error(this.promptInfo.layers_min_length);
            }
            this.lines[index].coordinates = newCoordinates;
            this._updateCanvas(true);
          } else {
            throw new Error(this.promptInfo.layers_no);
          }
        } else {
          throw new Error(this.promptInfo.layers_no);
        }
      } else {
        throw new Error(this.promptInfo.layers_data_none);
      }
    } catch (error) {
      console.error(error);
    }
  },
  /**
   * 更新数据
   * @param {String} layerId 线条索引
   * @param {Array} newCoordinates 新坐标
   **/
  updateLineData(layerId, newCoordinates = []) {
    try {
      if (!layerId) {
        throw new Error(this.promptInfo.layerid_null);
      }
      if (layerId && newCoordinates.length > 0) {
        if (this.lines.length > 0) {
          let index = this.lines.findIndex((e) => e.id === layerId);
          if (index !== -1) {
            this.lines[index].coordinates.push(...newCoordinates);
            this._updateCanvas();
          } else {
            throw new Error(this.promptInfo.layers_no);
          }
        } else {
          throw new Error(this.promptInfo.layers_no);
        }
      } else {
        throw new Error(this.promptInfo.layers_data_none);
      }
    } catch (error) {
      console.error(error);
    }
  },
  /**
   * 更新线条样式
   * @param {String} color - 线条颜色
   * @param {String} weight - 线条宽度
   */
  updateLineStyle: function(layerId = '', config = {}) {
    try {
      if (!layerId) {
        throw new Error(this.promptInfo.layerid_null);
      }
      if (layerId && JSON.stringify(config) !== '{}') {
        if (this.lines.length > 0) {
          let index = this.lines.findIndex((e) => e.id === layerId);
          if (index !== -1) {
            this.lines[index].color = config.color || this.lines[index].color;
            this.lines[index].weight = config.weight ||
                this.lines[index].weight;
            this._updateCanvas();
          } else {
            throw new Error(this.promptInfo.layers_no);
          }
        } else {
          throw new Error(this.promptInfo.layers_no);
        }
      }
    } catch (error) {
      console.error(error);
    }
  },
});
L.canvasLine = function(option) {
  return new LeafletCanvasLine(option);
};
export default L.canvasLine;
